﻿using System;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Threading;

using FileTreeSpace;

namespace TravModelSpace
{
    public class DuplSortItem : IComparable<DuplSortItem>
    {
        long size;
        int index;
        public string FileName { get; set; }
        public string Hash { get; set; }

        public DuplSortItem (long newSize, int newIndex) { size = newSize; index = newIndex; FileName = null; }
        public DuplSortItem (int newIndex, long newSize, string newName, string newHash)
        { index = newIndex; size = newSize; FileName = newName; Hash = newHash; }

        public long Size { get { return size; } }
        public int Index { get { return index; } }
        public int CompareTo (DuplSortItem other)
        {
            if (size < other.size)
                return -1;
            else if (size > other.size)
                return 1;
            else
                return index - other.index;
        }
    }

    internal class DuplSort
    {
        private List<DuplSortItem> list;

        public DuplSort () { list = new List<DuplSortItem> (); }

        public int Count { get { return list.Count; } }
        public DuplSortItem this[int index] { get { return list[index]; } }
        public long TotalSize { get; private set; }
        public int TotalCount { get; private set; }

        public void Add (DuplSortItem item)
        {
            list.Add (item);
        }

        public void Sort ()
        {
            list.Sort ();
            TotalSize = 0;
            TotalCount = 0;

            if (list.Count < 2)
                return;

            long size9 = list[0].Size;

            for (int i0 = 1; i0 < list.Count; ++i0)
            {
                if (list[i0].Size == size9)
                {
                    if (list[i0 - 1].Hash == null)
                    {
                        TotalSize += size9;
                        ++TotalCount;
                    }
                    for (; ; )
                    {
                        if (list[i0].Hash == null)
                        {
                            TotalSize += size9;
                            ++TotalCount;
                        }
                        ++i0;
                        if (i0 >= list.Count)
                            return;
                        if (list[i0].Size != size9)
                            break;
                    }
                }
                size9 = list[i0].Size;
            }
        }

        // On entry:  list has been presorted.
        // On exit: yields unhashed files with size same as another file.
        public IEnumerable ItemsToHash
        {
            get
            {
                if (list.Count > 1)
                {
                    long prevSize = list[0].Size;

                    for (int i = 1; i < Count; ++i)
                    {
                        if (list[i].Size == prevSize)
                        {
                            if (list[i - 1].Hash == null)
                                yield return list[i - 1];

                            for (; ; )
                            {
                                if (list[i].Hash == null)
                                    yield return list[i];

                                if (++i >= Count)
                                    yield break;

                                if (list[i].Size != prevSize)
                                    break;
                            }
                        }
                        prevSize = list[i].Size;
                    }
                }
            }
        }
    }


    public partial class TravModel
    {
        private void finderWork (object sender, DoWorkEventArgs e)
        {
            BackgroundWorker thread = (BackgroundWorker) sender;
            DuplSort list = (DuplSort) e.Argument;

            int hashTotalCount = 0;
            long hashTotalSize = 0;
            int hashSoFarCount = 0;
            long hashSoFarSize = 0;

            foreach (DuplSortItem item in list.ItemsToHash)
            { ++hashTotalCount; hashTotalSize += item.Size; }

            foreach (DuplSortItem item in list.ItemsToHash)
            {
                if (thread.CancellationPending)
                {
                    e.Cancel = true;
                    return;
                }

                string s = String.Format (@"Hashing {0}/{1}: {2}",
                    hashSoFarCount + 1, hashTotalCount, item.FileName);

                if (hashTotalSize > 0)
                    thread.ReportProgress ((int) ((hashSoFarSize * 100) / hashTotalSize), s);

                DirPath path = new DirPath (Tree, item.FileName);
                path.CalcHash ();
                item.Hash = path.File.Hash.ToString ();

                ++hashSoFarCount;
                hashSoFarSize += item.Size;
            }

            e.Result = list;
        }


        private void finderProgressed (object sender, ProgressChangedEventArgs args)
        {
            string s = args.UserState as string;
            if (s != null)
                Progress.Caption = s;

            Progress.SetMeter (args.ProgressPercentage);
        }


        private void finderCompleted (object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                Presenter.ShowMessage (e.Error.Message, "IMPOSSIBLE ERROR");
                return;
            }

            Progress.Dismiss ();

            if (e.Cancelled)
                return;

            DuplSort list = e.Result as DuplSort;
            int i0 = 0;
            int j0 = list[i0].Index;
            long size = Detail.Presenter.FileSize (j0);
            string hash = list[i0].Hash;
            if (hash != null)
                // CalcHash has already updated Tree
                Detail.Presenter.SetHash (j0, hash);
            else
                hash = Detail.Presenter.Hash (j0);

            for (; i0 < list.Count; )
            {
                Detail.Presenter.SetIsSelected (j0, false);

                for (; ; )
                {
                    ++i0;
                    if (i0 >= list.Count)
                        break;
                    int j1 = list[i0].Index;

                    string h1 = list[i0].Hash;
                    if (h1 != null)
                        Detail.Presenter.SetHash (j1, h1);
                    else
                        h1 = Detail.Presenter.Hash (j1);

                    long size11 = Detail.Presenter.FileSize (j1);
                    if (size11 != size)
                    {
                        j0 = j1;
                        size = size11;
                        hash = h1;
                        break;
                    }

                    Detail.Presenter.SetIsSelected (j1, h1 == hash);
                }
            }
        }

    }
}
